#include <func.h>
#include"client.h"
#include "account.h"
#include "virtualFileSystem.h"
#include "threadPool.h"
#include "taskQueue.h"
int main(int argc,char *argv[]){
    //./client 192.168.4.128 1234
    ARGS_CHECK(argc,3);
    int sockFd = socket(AF_INET,SOCK_STREAM,0);
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(atoi(argv[2]));
    addr.sin_addr.s_addr = inet_addr(argv[1]);
    int ret =connect(sockFd,(struct sockaddr*)&addr,sizeof(addr));
    ERROR_CHECK(ret,-1,"connect");

    //线程初始化
    threadPool_t threadPool;
    threadPoolInit(&threadPool,5);
    //创建5个线程用于上传以及下载
    makeWorker(&threadPool);
    //初始化一个token，用于服务端判断第几次连接以及是哪个用户连接
    char token[300] = {0};
    strcpy(token,"first");
    train_t t;
    //发送第一次登录的token值
    bzero(&t,sizeof(t));
    t.dataLength = strlen(token);
    strcpy(t.buf,token);
    send(sockFd,&t,sizeof(t.dataLength) + t.dataLength,0);
    //用户登录
    char *userName = (char *)calloc(100,sizeof(char));
    loadClient(sockFd,&userName);
    int path[1024] = {0};
    //发送登录日期，用于服务端生成token码，后续改为真实时间
    char time[30] = "2022/3/18";
    bzero(&t,sizeof(t));
    t.dataLength = strlen(time);
    strcpy(t.buf,time);
    send(sockFd,&t,sizeof(t.dataLength)+t.dataLength,0);
    //接收token码
    bzero(&t,sizeof(t));
    bzero(token,sizeof(token));
    recv(sockFd,&t.dataLength,sizeof(t.dataLength),0);
    printf("recv datalength = %d\n",t.dataLength);
    recv(sockFd,token,t.dataLength,0);
    //打印一下token用于测试
    puts(token);
    //功能
    while(1){
        //int command = startClient();
        int command = 1;
        printf("\n可使用的指令编号有:\n");
        printf("1. ls 列出相应目录文件\n");
        printf("2. cd 进入对应目录\n");
        printf("3. pwd 显示目前所在路径\n");
        printf("4. puts 将本地文件上传至服务器\n");
        printf("5. gets 下载服务器文件到本地\n");
        printf("6. remove 删除服务器上文件\n");
        printf("7. 查看日志\n");
        printf("请输入一个指令编号\n");
        ret = scanf("%d",&command);
        while(ret == 0){
            printf("输入错误,请重新输入！\n"); 
            ret = scanf("%d",&command); 
            getchar();
        }

        char command_buf[1024]={0};
        sprintf(command_buf,"%d",command);
        ret = send(sockFd,command_buf,sizeof(command_buf),MSG_NOSIGNAL);
        if(ret == -1){
            puts("服务器端关闭");
            break;
        }

        char buf[1024]={0};
        switch (command)
        {
            case 1:
                func1(sockFd);//ls 列出相应目录文件
                break;

            case 2:
                func2(sockFd);// cd 进入对应目录
                break;

            case 3:
                func3(sockFd);//pwd 显示目前所在路径
                break;

            case 4:
                func4_upload(&threadPool,&addr,token,userName,time);//puts 将本地文件上传至服务器
                sleep(1);
                break;

            case 5:{
                func5_download(&threadPool,&addr,token,userName,time);//gets 将服务器文件下载到本地中
                sleep(1);
                //func5(sockFd);//gets 文件名 下载服务器文件到本地
                break;
            }

            case 6:{
                
                func6(sockFd);//remove 删除服务器上文件
                break;
            }

            case 7:
                func7(sockFd);//查看日志
                break;
        
            default:
                break;
        }
        getchar();
        printf("请输入任意指令以继续!\n");
        getchar();
    }

    printf("感谢使用！\n");
    close(sockFd);
    free(userName);
}

int func1(int netFd){
    printf("ll结果:\n");
    lsClient(netFd);
}

int func2(int netFd){
    printf("请输入目录名:\n");
    char fileName[1024]={0};
    scanf("%s",fileName);
    train_t t;
    //发送目录名
    bzero(&t,sizeof(t));
    t.dataLength = strlen(fileName);
    strcpy(t.buf,fileName);
    send(netFd,&t,sizeof(t.dataLength) + t.dataLength,0);


    /* getchar();
    //puts(buf);
    fgets(buf,sizeof(buf),stdin);
    puts(buf);
    send(netFd,buf,sizeof(buf),0);
    bzero(buf,sizeof(buf));
    printf("cd后目录为:\n");
    recv(netFd,buf,sizeof(buf),0);
    puts(buf); */
}

int func3(int netFd){
    printf("\n当前目录为:");
    int dataLength;
    char buf[1024]={0};
    //接收路径
    recv(netFd,&dataLength,sizeof(dataLength),0);
    recv(netFd,buf,dataLength,0);
/*     bzero(buf,sizeof(buf));
    puts(buf);
    recv(netFd,buf,sizeof(buf),0); */
    puts(buf);
}

int func4_upload(threadPool_t *pThreadPool,struct sockaddr_in *addr,char* token,char *userName,char *loadtime){
    printf("请输入要上传的文件名：\n");
    char fileName[1024]={0};
    scanf("%s",fileName);
    pthread_mutex_lock(&pThreadPool->mutex);
    enQueue(&pThreadPool->taskQueue,4,addr,token,fileName,userName,loadtime);//创建一个工作队列
    puts("uploading");
    pthread_cond_signal(&pThreadPool->cond);
    pthread_mutex_unlock(&pThreadPool->mutex);
    //sendFile(send_file,netFd);

}


int func5_download(threadPool_t *pThreadPool,struct sockaddr_in *addr,char* token,char *userName,char *loadtime){
    printf("请输入要下载的文件名：\n");
    char fileName[1024]={0};
    scanf("%s",fileName);
    pthread_mutex_lock(&pThreadPool->mutex);
    enQueue(&pThreadPool->taskQueue,5,addr,token,fileName,userName,loadtime);//创建一个工作队列
    puts("uploading");
    pthread_cond_signal(&pThreadPool->cond);
    pthread_mutex_unlock(&pThreadPool->mutex);
    //sendFile(send_file,netFd);

}


/* int func5(int netFd){
    printf("请输入要下载的文件名:\n");
    char name[1024]={0};
    scanf("%s",name);
    downClient(netFd,name);
    // send(netFd,buf,sizeof(buf),0);
    //recvFile(netFd);
} */

int func6(int netFd){
    printf("请输入要删除的文件名:\n");
    char buf[1024]={0};
    scanf("%s",buf);
    send(netFd,buf,sizeof(buf),0);
    bzero(buf,sizeof(buf));
    recv(netFd,buf,sizeof(buf),0);
    puts(buf);
}

int func7(int netFd){
    char buf[40960] = {0};
    recv(netFd,buf,sizeof(buf),0);
    puts(buf);
}

int sendFile(char filename[],int sdf)
{
   train_t t;
   int fd=open(filename,O_RDWR);
   bzero(&t,sizeof(t));
   struct stat st;
   int retstat=fstat(fd,&st);
   ERROR_CHECK(retstat,-1,"fstat");
   long localsize=st.st_size; //服务端被下载文件大小
   printf("服务端被下载文件大小%ldM\n",localsize/M);
   t.dataLength=sizeof(st.st_size);
   memcpy(t.buf,&st.st_size,sizeof(st.st_size));
   int ret6=send(sdf,&t,t.dataLength+sizeof(t.dataLength),MSG_NOSIGNAL);//发送接收端文件的大小长度+文件大小
   ERROR_CHECK(ret6,-1,"发送服务端被下载文件的大小长度与大小");
   long filesize;
   int dataLength;
   int ret1=recv(sdf,&dataLength,sizeof(int),MSG_WAITALL); //接收客户端同名文件大小长度
   int ret2=recv(sdf,&filesize,dataLength,MSG_WAITALL); //接收客户端同名文件大小
   ERROR_CHECK(ret1,-1,"接收服客户端同名文件大小长度");
   char* p=(char*)mmap(NULL,st.st_size,PROT_READ,MAP_SHARED,fd,0);
   if(p!=NULL)
   {
     printf("mmap成功\n");
   }
   off_t index=0;
   if(filesize!=0)
   {
      printf("断点重传\n");
      printf("客户端同名文件大小%ldM\n",filesize/M);
      index=filesize;
   }
   int i=0;
   while(index<localsize)
   {
     if(localsize-index >= sizeof(t.buf))
     {
       t.dataLength=sizeof(t.buf);
     }
     else
     {
       t.dataLength=localsize-index;
     }
       memcpy(t.buf,p+index,t.dataLength);
       index=index+t.dataLength;
       int ret7=send(sdf,&t,sizeof(int)+t.dataLength,MSG_NOSIGNAL);
    }
    t.dataLength=0;
    send(sdf,&t,sizeof(int),MSG_NOSIGNAL);
    int ret8=munmap(p,st.st_size);
    ERROR_CHECK(ret8,-1,"解除mmap映射");
    printf("发送文件大小: %ldM\n",index/M);
    close(fd);
    return 0;
   }
int recvFile(int netFd ,char *fileName){
    train_t t;
    //打开或者创建临时文件
    int fd = open(fileName,O_RDWR|O_CREAT,0666);
    ERROR_CHECK(fd,-1,"open");
    bzero(&t,sizeof(t));
    long fileSize;
    //获取描述文件总大小的火车头
    int ret = recv(netFd,&t.dataLength,sizeof(t.dataLength),MSG_WAITALL);
    ERROR_CHECK(ret,-1,"recv dataLength");
    //再获取文件总大小
    recv(netFd,&fileSize,t.dataLength,MSG_WAITALL);
    ERROR_CHECK(ret,-1,"recv fileSize");
    printf("目标文件大小: %ldM\n",fileSize/M);
    //获取临时文件总大小，并发送给发送端
    struct stat statbuf;
    ret = fstat(fd,&statbuf);
    ERROR_CHECK(ret,-1,"fstat");
    bzero(&t,sizeof(t));
    t.dataLength = sizeof(statbuf.st_size);
    //把文件大小放到小火车车厢中
    memcpy(t.buf,&statbuf.st_size,sizeof(statbuf.st_size));
    //再发送文件大小
    send(netFd,&t,sizeof(t.dataLength)+t.dataLength,MSG_NOSIGNAL);
    off_t downSize = statbuf.st_size;//当前已经下载文件的大小
    printf("当前文件大小:%ldM\n",downSize/M);
    off_t needSize = (fileSize - downSize)/M;//需下载的量
    printf("需下载:%ldM\n",needSize);
    off_t lastSize = statbuf.st_size;//在上一次完成下载文件总大小千分之一时的文件大小
    off_t slice = fileSize/1000;//总文件大小千分之一
    //设置当前文件偏移量，让其在之前数据之后读写
    if(statbuf.st_size != 0){
        lseek(fd,downSize,SEEK_SET);
    }
        while(1){
             bzero(&t,sizeof(t));
             ret = recv(netFd,&t.dataLength,sizeof(int),MSG_WAITALL);
             ERROR_CHECK(ret,-1,"recv dataLength");
             if(t.dataLength == 0){
                 break;
             }
             downSize += t.dataLength;
             if(downSize - lastSize > slice){
                 double precent = (double)downSize/fileSize;
                 printf("[%5.2lf%%]\r",100.0*precent);
                 fflush(stdout);
             }
             ret = recv(netFd,t.buf,t.dataLength,MSG_WAITALL);
             ERROR_CHECK(ret,-1,"recv t.buf");
             write(fd,t.buf,t.dataLength);
        }
    printf("[100.00%s]\n","%");
    close(fd);
}
    //savelog(__FUNC